<!DOCTYPE html>
<meta charset="utf-8">
<title>力导向图</title>
<style>

    .node {
        cursor: pointer;
        stroke: #3182bd;
        stroke-width: 1.5px;
    }

    .link {
        fill: none;
        stroke: #9ecae1;
        stroke-width: 1.5px;
    }

</style>
<body style="overflow-x: hidden ;overflow-y: hidden">
<script src="VIS/dist/d3.v3.min.js"></script>
<script>

    var width = 1560,
        height = 800,
        root;

    var force = d3.layout.force()
        // .size([width, height])
        // .on("tick", tick);
        .size([width, height])//作用域的大小
        .linkDistance(150)//连接线长度
        .charge(-1500)//顶点的电荷数。该参数决定是排斥还是吸引，数值越小越互相排斥
        .friction(0.9)
        .on("tick", tick)//指时间间隔，隔一段时间刷新一次画面
        .start();//开始转换
    var zoom = d3.behavior.zoom()//缩放配置，
        .scaleExtent([0.4, 2])//缩放比例
        .on("zoom", zoomed);

    function zoomed() {//缩放函数
// .selectAll("g")
        svg.selectAll("g").attr("transform",//svg下移动大小
            //
            "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
        /*console.log(d3.event.translate+"/"+d3.event.scale);*/
    }

    var svg = d3.select("body").append("svg")
        .attr("pointer-event", "all")
        // .attr("width", width)
        // .attr("height", height);
        .attr("preserveAspectRatio", "xMidYMid meet")
        .attr("viewBox", "0 0 1920 1000")
        .style('background-color', '#fafbfc')
        // .append("g")
        .call(zoom)


    //添加标记以表明这是一个有向图
    svg.append("g").append("defs").selectAll("marker")
        .data(["arrow"])
        .enter().append("marker")
        .attr("id", function (d) {
            return d;
        })
        .attr("viewBox", "0 -5 10 10")
        .attr("refX", 25)
        .attr("refY", -1)
        .attr("markerWidth", 10)
        .attr("markerHeight", 12)
        .attr("orient", "auto")
        .append("path")
        .attr("stroke-width", 2)//箭头宽度
        .attr("d", "M0,-5L10,0L0,5")
        .attr('fill', '#C0C0C0');//箭头颜色;

    // svg.append("defs").append("g").selectAll(".edgepath")
    var link = svg.append("g").selectAll(".link"),
        node = svg.append("g").selectAll(".node"),
        svg_text = svg.append("g").selectAll('text'),
        svg_path = svg.append("g").selectAll("path")
    ;
    // var svg_edgepath = svg.selectAll(".edgepath");
    jsontest = {
        "nodes": [{'group': '知识图谱', 'id': 0, 'label': '知识图谱'}, {
            'group': '关键词',
            'id': 1,
            'label': '关键词'
        }, {'group': 'Event', 'id': 2, 'label': '反映动向'}, {
            'group': 'Event',
            'id': 3,
            'label': '纳入其中'
        }, {'group': 'Event', 'id': 4, 'label': '针对中国'}, {
            'group': 'Event',
            'id': 5,
            'label': '提出动机'
        }, {'group': 'Event', 'id': 6, 'label': '作为国家'}, {'group': 'Event', 'id': 7, 'label': '提框架'}, {
            'group': 'Event',
            'id': 8,
            'label': '提出协定'
        }, {'group': 'Event', 'id': 9, 'label': '退群'}, {'group': 'Event', 'id': 10, 'label': '上台'}, {
            'group': 'Event',
            'id': 11,
            'label': '基于'
        }, {'group': 'Event', 'id': 12, 'label': '增加金白银'}, {
            'group': 'Event',
            'id': 13,
            'label': '为了'
        }, {'group': 'Event', 'id': 14, 'label': '跳出考量'}, {
            'group': 'Event',
            'id': 15,
            'label': '摆脱思维'
        }, {'group': 'Event', 'id': 16, 'label': '还是美国'}, {
            'group': 'Event',
            'id': 17,
            'label': '经济'
        }, {'group': 'Event', 'id': 18, 'label': '框架'}, {'group': 'Event', 'id': 19, 'label': '地区'}, {
            'group': 'Event',
            'id': 20,
            'label': '合作'
        }, {'group': 'Event', 'id': 21, 'label': '政府'}, {'group': 'Event', 'id': 22, 'label': '战略'}, {
            'group': 'Event',
            'id': 23,
            'label': '提出'
        }, {'group': '高频词', 'id': 24, 'label': '高频词'}, {'group': '机构', 'id': 25, 'label': '机构'}, {
            'group': '地名',
            'id': 26,
            'label': '地名'
        }, {'group': 'Event', 'id': 27, 'label': '拜登'}, {'group': '人物', 'id': 28, 'label': '人物'}, {
            'group': 'Event',
            'id': 29,
            'label': '长雷蒙多'
        }, {'group': 'Event', 'id': 30, 'label': '亚洲'}, {
            'group': 'Event',
            'id': 31,
            'label': '美国商务部'
        }, {'group': 'Event', 'id': 32, 'label': '长雷'}, {'group': 'Event', 'id': 33, 'label': '印太'}, {
            'group': 'Event',
            'id': 34,
            'label': '太平洋'
        }, {'group': 'Event', 'id': 35, 'label': '美国'}, {'group': 'Event', 'id': 36, 'label': '中国'}]


        ,
        "links": [{'source': 0, 'label': '', 'target': 1}, {'source': 0, 'label': '', 'target': 24}, {
            'source': 0,
            'label': '',
            'target': 28
        }, {'source': 0, 'label': '', 'target': 25}, {'source': 0, 'label': '', 'target': 26}, {
            'source': 1,
            'label': '',
            'target': 17
        }, {'source': 17, 'label': '', 'target': 20}, {'source': 17, 'label': '', 'target': 20}, {
            'source': 24,
            'label': '',
            'target': 17
        }, {'source': 1, 'label': '', 'target': 18}, {'source': 18, 'label': '', 'target': 2}, {
            'source': 18,
            'label': '',
            'target': 3
        }, {'source': 18, 'label': '', 'target': 4}, {'source': 24, 'label': '', 'target': 18}, {
            'source': 1,
            'label': '',
            'target': 19
        }, {'source': 19, 'label': '', 'target': 20}, {'source': 24, 'label': '', 'target': 19}, {
            'source': 1,
            'label': '',
            'target': 20
        }, {'source': 24, 'label': '', 'target': 20}, {'source': 1, 'label': '', 'target': 21}, {
            'source': 21,
            'label': '',
            'target': 5
        }, {'source': 21, 'label': '', 'target': 10}, {'source': 24, 'label': '', 'target': 21}, {
            'source': 1,
            'label': '',
            'target': 22
        }, {'source': 22, 'label': '', 'target': 14}, {'source': 24, 'label': '', 'target': 22}, {
            'source': 24,
            'label': '',
            'target': 23
        }, {'source': 1, 'label': '', 'target': 27}, {'source': 26, 'label': '', 'target': 27}, {
            'source': 28,
            'label': '',
            'target': 27
        }, {'source': 28, 'label': '', 'target': 29}, {'source': 29, 'label': '', 'target': 31}, {
            'source': 29,
            'label': '',
            'target': 30
        }, {'source': 26, 'label': '', 'target': 30}, {'source': 30, 'label': '', 'target': 29}, {
            'source': 30,
            'label': '',
            'target': 31
        }, {'source': 31, 'label': '', 'target': 25}, {'source': 31, 'label': '', 'target': 29}, {
            'source': 31,
            'label': '',
            'target': 30
        }, {'source': 31, 'label': '', 'target': 32}, {'source': 28, 'label': '', 'target': 32}, {
            'source': 32,
            'label': '',
            'target': 31
        }, {'source': 1, 'label': '', 'target': 33}, {'source': 24, 'label': '', 'target': 33}, {
            'source': 26,
            'label': '',
            'target': 33
        }, {'source': 33, 'label': '', 'target': 35}, {'source': 26, 'label': '', 'target': 34}, {
            'source': 34,
            'label': '',
            'target': 35
        }, {'source': 1, 'label': '', 'target': 35}, {'source': 35, 'label': '', 'target': 6}, {
            'source': 35,
            'label': '',
            'target': 7
        }, {'source': 35, 'label': '', 'target': 8}, {'source': 35, 'label': '', 'target': 9}, {
            'source': 35,
            'label': '',
            'target': 11
        }, {'source': 35, 'label': '', 'target': 13}, {'source': 35, 'label': '', 'target': 12}, {
            'source': 35,
            'label': '',
            'target': 13
        }, {'source': 35, 'label': '', 'target': 15}, {'source': 35, 'label': '', 'target': 16}, {
            'source': 24,
            'label': '',
            'target': 35
        }, {'source': 26, 'label': '', 'target': 35}, {'source': 35, 'label': '', 'target': 33}, {
            'source': 35,
            'label': '',
            'target': 34
        }, {'source': 35, 'label': '', 'target': 36}, {'source': 1, 'label': '', 'target': 36}, {
            'source': 24,
            'label': '',
            'target': 36
        }, {'source': 26, 'label': '', 'target': 36}, {'source': 36, 'label': '', 'target': 35}]


    }
    jsonmethod(jsontest);

    function jsonmethod(json) {
        // d3.json("graph.json", function (json) {
        root = jsontest;
        // root = json;
        //给出节点id和初始化变量
        for (var i = 0; i < root.nodes.length; i++) {
            var node = root.nodes[i];
            // node.id = i;
            node.id = root.nodes[i].id;
            // console.log(root.nodes[i].name)
            node.name = root.nodes[i].label
            node.collapsing = 0;
            node.collapsed = false;
        }
        //给出链接id和初始化变量
        for (var i = 0; i < root.links.length; i++) {
            var link = root.links[i];
            link.source = root.nodes[link.source];
            link.target = root.nodes[link.target];
            link.id = i;
        }

        update();

    }

    function update() {
        //只保留可见的节点
        var nodes = root.nodes.filter(function (d) {
            return d.collapsing === 0;
        });
        var links = root.links;
        //只保留可见的链接
        links = root.links.filter(function (d) {
            return d.source.collapsing === 0 && d.target.collapsing === 0;
        });

        force
            .nodes(nodes)
            .links(links)
            .start();

        // Update the links…
        link = link.data(links, function (d) {
            return d.id;
        });
        // link.enter().append("g").append("circle")

        // Exit any old links.
        link.exit().remove();

        // Enter any new links.
        link.enter().insert("line", ".node")
            .attr("class", "link")
            .attr("x1", function (d) {
                return d.source.x;
            })
            .attr("y1", function (d) {
                return d.source.y;
            })
            .attr("x2", function (d) {
                return d.target.x;
            })
            .attr("y2", function (d) {
                return d.target.y;
            })
            .attr("marker-end", "url(#arrow)");

        // Update the nodes…
        node = node.data(nodes, function (d) {
            return d.id;
        });

        // Exit any old nodes.
        node.exit().remove();

        // Enter any new nodes.
        node.enter().append("circle")
            .attr("class", "node")
            .attr("cx", function (d) {
                return d.x;
            })
            .attr("cy", function (d) {
                return d.y;
            })
            .attr("r", function (d) {
                // return Math.sqrt(d.size) / 10 || 4.5;
                return Math.sqrt(d.size) / 10 || 25;
            })
            // .style("fill", color)
            .style("fill", function (node) {
                var color;//圆圈背景色
                var link = links[node.index];
                if (node.group === "知识图谱") {
                    color = "#fd8d3c"
                    // } else if (node.group === "关键词"){|| node.group === "高频词" || node.group === "地名" || node.group === "人物" || node.group === "组织机构") {
                } else if (node.group === "关键词") {
                    // function color(d) {
                    //     return d.collapsed ? "#3182bd" : "#fd8d3c";
                    // }
                    // color()
                    color = "#fafbfc";
                } else if (node.group === "人物") {
                    color = "#fafbfc";
                } else if (node.group === "机构") {
                    color = "#fafbfc";
                } else if (node.group === "地名") {
                    color = "#fafbfc";
                } else if (node.group === "高频词") {
                    color = "#fafbfc";
                } else {
                    color = "#97c2fc";
                }
                return color;
            })
            .on("click", click)
            // .attr("r", 25)//设置圆圈半径
            // .attr('dominant-baseline', 'middle')
            .call(force.drag);


        svg_text = svg_text.data(nodes, function (d) {
            return d.name;
        })
        svg_text.exit().remove();


        // text
        svg_text.enter()
            // .append("g")
            .append('text')
            // .style('fill', '#000')
            .attr('dominant-baseline', 'middle')
            .attr('text-anchor', 'middle')
            .style('fill', function (node) {
                var color;//文字颜色
                color = "#000"
                return color;
            })
            .text(function (d) {
                return d.name
            });
        svg.on("dblclick.zoom", null);
        svg_text
            .attr('x', function (d) {
                return d.x
            })
            .attr('y', function (d) {
                return d.y
            })
            .attr('dominant-baseline', 'middle')
            .attr('text-anchor', 'middle')
            .text(function (d) {
                return d.name
            });
    }

    function tick() {
        link.attr("x1", function (d) {
            return d.source.x;
        })
            .attr("y1", function (d) {
                return d.source.y;
            })
            .attr("x2", function (d) {
                return d.target.x;
            })
            .attr("y2", function (d) {
                return d.target.y;
            });

        node.attr("cx", function (d) {
            return d.x;
        })
            .attr("cy", function (d) {
                return d.y;
            });
        svg_text
            .attr('x', function (d) {
                return d.x
            })
            .attr('y', function (d) {
                return d.y
            });
    }


    // 将叶子节点涂成橙色，包裹为白色或蓝色 .
    // function color(d) {
    //     return d.collapsed ? "#3182bd" : d.children ? "#c6dbef" : "#fd8d3c";
    // }

    Array.prototype.del = function (index) {
        if (isNaN(index) || index >= this.length) {
            return false;
        }
        for (var i = 0; n = 0; i++) {
            if (this[i] != this[index]) {
                this[n++] = this[i];
            }
        }
        this.length -= 1;
    }
    //递归处理所有子节点 falg true ++ false --
    var tmpLink = []

    function recursion(d, data, flag) {
        if (data.length > 0) {
            data.forEach(el => {
                if (flag) {
                    //折叠记录
                    if (typeof (el['_nodeId']) == 'undefined') {
                        el['_nodeId'] = []
                    }
                    el['_nodeId'].push(d.id)
                } else {
                    //删除记录
                    if (el['_nodeId']) {
                        el['_nodeId'].del(el['_nodeId'].indexOf(d.id))
                    }
                }
                flag ? el.target.collapsing++ : el.target.collapsing--
                tmpLink.push(el.target.id)
                var tmp = root.links.filter(e => e.source.id === el.target.id && el.target.id !== d.id)
                tmp = tmp.filter(el => {
                    var a = tmpLink.find(e => e == el.target.id)
                    return a ? false : true
                })
                if (!flag) {
                    tmp = tmp.filter(el => {
                        return typeof (el['_nodeId']) != 'undefined' && el._nodeId.filter(e => e == d.id).length > 0
                    })
                } else {
                    tmp = tmp.filter(el => {
                        var a = root.nodes.find(e => e.id == el.target.id)
                        return a.collapsing == 0
                    })
                }
                recursion(d, tmp, flag)
            })
        }

    }

    // 在点击中切换孩子.
    function click(d) {
        console.log("click节点", d)
        if (!d3.event.defaultPrevented) {
            //过滤 sourceId 为 点击节点的 links 数组
            var relevantLinks = root.links.filter(el => el.source.id === d.id)
            if (d.collapsed) {
                //显示处理
                relevantLinks = relevantLinks.filter(el => {
                    return typeof (el['_nodeId']) != 'undefined' && el._nodeId.filter(e => e == d.id).length > 0
                })
                tmpLink = [d.id]
                recursion(d, relevantLinks, false)
            } else {
                //折叠处理
                relevantLinks = relevantLinks.filter(el => {
                    var a = root.nodes.find(e => e.id == el.target.id)
                    return a.collapsing == 0
                })
                tmpLink = [d.id]
                recursion(d, relevantLinks, true)
            }
            d.collapsed = !d.collapsed;
        }

        // console.log("结果", root)
        update();
    }

</script>